{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://raw.githubusercontent.com/Qiskit/qiskit-tutorials/master/images/qiskit-heading.png\" alt=\"Note: In order for images to show up in this jupyter notebook you need to select File => Trusted Notebook\" width=\"500 px\" align=\"left\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "# _*Hello Qiskit*_\n",
    "\n",
    "Click [here](https://mybinder.org/v2/gh/QISKit/qiskit-tutorial/master?filepath=community/games/Hello_Qiskit.ipynb) to run this notebook in your browser using [Binder](https://mybinder.org).\n",
    "\n",
    "The latest version of this notebook is available on https://github.com/qiskit/qiskit-tutorial.\n",
    "\n",
    "***\n",
    "### Contributors\n",
    "James R. Wootton, IBM Research\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Level 1: Beginning with bits\n",
    "\n",
    "This is a Jupyter notebook, in which you will run some puzzles and learn about quantum computing. Don't worry if you've never used a Jupyter notebook before. It just means you'll see lots of grey boxes with code in, like the one below. These are known as cells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Hello! I'm a code cell\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll need to run the cells to use this tutorial. To run a cell, do the following.\n",
    "\n",
    "* For laptops and desktops, click on the cell and press **Shift-Enter**.\n",
    "\n",
    "* For mobile devices, tap on the icon that appears to the left of a cell.\n",
    "\n",
    "Get started by doing this for the cell below (it will take a second or two to run)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Set up started...')\n",
    "%matplotlib notebook\n",
    "import sys\n",
    "sys.path.append('game_engines')\n",
    "import hello_quantum\n",
    "print('Set up complete!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The rest of the cells in this notebook contain code that sets up puzzles for you to solve. To get the puzzles, just run the cells. To restart a puzzle, just rerun it.\n",
    "\n",
    "If you want to know what the code is doing, or to create your own puzzles, see the guide [here](helper_functions/Making_your_own_hello_quantum.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1\n",
    "\n",
    "### Intro\n",
    "* Normal computers are made of bits, and quantum computers are made of qubits.\n",
    "* Qubits are basically an upgraded version of bits, so let's make sure we understand basic bit-based programming first.\n",
    "* The defining feature of a bit is that it has two possible output values.\n",
    "* These are often called `1` and `0`, though we'll also be thinking of them as _on_ and _off_.\n",
    "* We use bits represent and process information, but we typically need lots of them to do this.\n",
    "* To help you understand how to manipulate bits, we'll give you one to play with.\n",
    "* The simplest thing you can do to a bit (other than leave it alone) is the `NOT` gate.\n",
    "* (The word 'gate' is used to describe basic computational commands)\n",
    "* Try it out and see what happens.\n",
    "\n",
    "### Exercise\n",
    "*  Use the `NOT` gate 3 times."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {}\n",
    "allowed_gates = {'0': {'NOT': 3}, '1': {}, 'both': {}}\n",
    "vi = [[1], False, False]\n",
    "qubit_names = {'0':'the only bit', '1':None}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Here our bit was depicted by a circle that was either on (white) or off (black).\n",
    "* The effect of the `NOT` gate was to turn it on and off.\n",
    "* This flips out bit between `0` and `1`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 2\n",
    "\n",
    "### Intro\n",
    "* Now let's do the same thing to a different bit.\n",
    "* This will look the same as before. But because it is a different bit, it'll be in a different place.\n",
    "\n",
    "### Exercise\n",
    "*  Turn the other bit on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {}\n",
    "allowed_gates = {'0': {}, '1': {'NOT': 0}, 'both': {}}\n",
    "vi = [[], False, False]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* You've now mastered the NOT gate: the most basic building block of computing."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 3\n",
    "\n",
    "### Intro\n",
    "* To really process information stored in bits, computers need more than just `NOT`s.\n",
    "* We need gates that let us manipulate some bits in a way that depends on other bits.\n",
    "* The simplest example is the controlled-`NOT`, or `CNOT`.\n",
    "* For this you need to choose one bit to be the _target_, and the other to be the _control_.\n",
    "* The `CNOT` then does a `NOT` on the target bit, but only if the control bit is on.\n",
    "\n",
    "### Exercise\n",
    "* Use the `CNOT` to turn on the bit on the right.\n",
    "* **Note**: The `CNOT` acts on both bits, but you still need to choose which will be the target bit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '0']]\n",
    "success_condition = {'IZ': -1.0}\n",
    "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n",
    "vi = [[], False, False]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 4\n",
    "\n",
    "### Intro\n",
    "* Now let's try something where you'll need a couple of `CNOT`s.\n",
    "\n",
    "### Exercise\n",
    "*  Use some CNOTs to turn the left bit off and the right bit on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '0']]\n",
    "success_condition = {'ZI': 1.0, 'IZ': -1.0}\n",
    "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n",
    "vi = [[], False, False]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Well done!\n",
    "* These kind of manipulations are what all computing compiles down to\n",
    "* With more bits and a controlled-controlled-`NOT`, you can do everything from Tetris to self-driving cars."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 5\n",
    "\n",
    "### Intro\n",
    "* Qubits have some similarities to random bit values, so let's take a few exercises to think about randomness.\n",
    "* For a bit that will give us a `0` or `1` with equal probability, we'll use a grey circle.\n",
    "\n",
    "### Exercise\n",
    "*  Make the bit on the right random using a CNOT."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0']]\n",
    "success_condition = {'IZ': 0.0}\n",
    "allowed_gates = {'0': {'CNOT': 0}, '1': {'CNOT': 0}, 'both': {}}\n",
    "vi = [[], False, False]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Well done!\n",
    "* If the left bit is off, the right bit stays off. If the left bit is on, the right bit gets switched on too.\n",
    "* So the random value of the left bit is effectively copied over to the right by the `CNOT`.\n",
    "* The end result was that both bits become random, but they nevertheless always output the same result.\n",
    "* This is not the only way of having two random bits. We could also create cases where they are independently random, or always have different results.\n",
    "* How can we keep track of how our random bits are correlated?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 6\n",
    "\n",
    "### Intro\n",
    "* To keep track of correlations, we'll add another circle.\n",
    "* This doesn't represent a new bit. It simply tells us whether our two bits will agree or not.\n",
    "* It will be off when they agree, on when they don't, and grey when they aren't correlated (so agreements and disagreements are random).\n",
    "\n",
    "### Exercise\n",
    "*  Make the two bits always disagree (that means making the middle circle white)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [['h', '0']]\n",
    "success_condition = {'ZZ': -1.0}\n",
    "allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}\n",
    "vi = [[], False, True]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 7\n",
    "\n",
    "### Intro\n",
    "* Now you know pretty much need all you need to know about bits.\n",
    "* Let's have one more exercise before we move on.\n",
    "\n",
    "### Exercise\n",
    "*  Turn on bit the bit on the right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '1']]\n",
    "success_condition = {'IZ': -1.0}\n",
    "allowed_gates = {'0': {'NOT': 0, 'CNOT': 0}, '1': {'NOT': 0, 'CNOT': 0}, 'both': {}}\n",
    "vi = [[], False, True]\n",
    "qubit_names = {'0':'the bit on the left', '1':'the bit on the right'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Time to move on to qubits!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Level 2: Basic single qubit gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1\n",
    "\n",
    "### Intro\n",
    "* There are many types of variables that you can have in a computer program.\n",
    "* Here we introduce you to a new one: the qubit.\n",
    "* Before we explain what they are, we'll give you one to play with.\n",
    "\n",
    "###  Exercise\n",
    "* Use the `x` gate 3 times, and see what happens"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [ [\"x\",\"0\"] ]\n",
    "success_condition = {\"ZI\":1.0}\n",
    "allowed_gates = { \"0\":{\"x\":3}, \"1\":{}, \"both\":{} }\n",
    "vi = [[1],True,True]\n",
    "qubit_names = {'0':'the only qubit', '1':None}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 2\n",
    "\n",
    "### Intro\n",
    "* You have now mastered turning a circle on and off :) \n",
    "* But what does that actually mean? What is it useful for? To answer that, you need to know something about qubits.\n",
    "* Basically, they are quantum objects from which we can extract a simple bit value: `0` or `1`.\n",
    "* There are many ways we can do this, and the result we get depends on the method we use.\n",
    "* The two circles in the last puzzle represent two different ways we could get a bit out of the same qubit.\n",
    "* They are usual called z measurements and x measurements, but we'll simply call them the top and bottom outputs in this tutorial.\n",
    "* A black circle means that the corresponding output would give the value `0`. A white one means we'd get a `1`.\n",
    "* The `x` gate has the effect of a `NOT` on the bottom output. It has no effect on the top.\n",
    "\n",
    "### Exercise\n",
    "*  Turn off the bottom circle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '0']]\n",
    "success_condition = {'ZI': 1.0}\n",
    "allowed_gates = {'0': {'x': 0}, '1': {}, 'both': {}}\n",
    "vi = [[1], True, True]\n",
    "qubit_names = {'0':'the only qubit', '1':None}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* The process of extracting a bit from a qubit is called 'measurement'. The `measure` gate in Qiskit always extracts the bit from the bottom output.\n",
    "* The top output is acquired indirectly, using the `h` gate that you will learn about soon."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 3\n",
    "\n",
    "### Intro\n",
    "* Now let's look at another qubit.\n",
    "* This will also have its inner workings represented by two circles.\n",
    "* But because it's a different qubit, these circles are in a different place.\n",
    "\n",
    "### Exercise\n",
    "*  Turn the bottom circle of the other qubit off."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '1']]\n",
    "success_condition = {'IZ': 1.0}\n",
    "allowed_gates = {'0': {}, '1': {'x': 0}, 'both': {}}\n",
    "vi = [[0], True, True]\n",
    "qubit_names = {'0':None, '1':'the other qubit'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* From now on, we'll start calling the qubits by more programming-friendly names.\n",
    "* The one on the left will be `q[0]`, and the one on the right will be `q[1]`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 4\n",
    "\n",
    "### Intro\n",
    "* Now it's time to try a new gate: the `h` gate.\n",
    "* This swaps the two circles of the qubit that it's applied to.\n",
    "* If you want to see this in a nice animated form, check out the [Hello Quantum](https://helloquantum.mybluemix.net/) app.\n",
    "* But while you are here, test it out with the old trick of repeating three times.\n",
    "\n",
    "### Exercise\n",
    "*  Use the `h` gate 3 times."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'ZI': 0.0}\n",
    "allowed_gates = {'0': {'h': 3}, '1': {}, 'both': {}}\n",
    "vi = [[1], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* This made the bottom circle become grey, which corresponds to a random output.\n",
    "* Note that you now have 'Your quantum program so far' being printed at the bottom.\n",
    "* This is the actual Qiskit code to create the circuit you made in the puzzle.\n",
    "* To see a circuit diagram of this, run the code cell below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 5\n",
    "\n",
    "### Intro\n",
    "* We know what `x` does to a circle that's fully off (it turns it on) or fully on (it turns it off).\n",
    "* But what does it do to one of these random grey ones?\n",
    "* By solving this exercise, you'll find out.\n",
    "\n",
    "### Exercise\n",
    "*  Get the bottom circle fully off. You can use as many `h` gates as you like, but use `x` exactly 3 times."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '1']]\n",
    "success_condition = {'IZ': 1.0}\n",
    "allowed_gates = {'0': {}, '1': {'x': 3, 'h': 0}, 'both': {}}\n",
    "vi = [[0], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* It turns out that a random result is just a random result, even if you flip it."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 6\n",
    "\n",
    "### Intro\n",
    "* Another important gate is called `z`.\n",
    "* This works similar to `x`, except that it acts as a `NOT` on the top circle instead of the bottom.\n",
    "\n",
    "### Exercise\n",
    "*  Turn the top circle off."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0'], ['z', '0']]\n",
    "success_condition = {'XI': 1.0}\n",
    "allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {}, 'both': {}}\n",
    "vi = [[1], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 7\n",
    "\n",
    "### Intro\n",
    "* The `z`, when combined with `h`, can be used to do the job of an `x`\n",
    "\n",
    "### Exercise\n",
    "*  Turn on the bottom circle without using the `x` gate\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'ZI': -1.0}\n",
    "allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {}, 'both': {}}\n",
    "vi = [[1], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 8\n",
    "\n",
    "### Intro\n",
    "* You might notice that the top circles are always random when the bottom circles are fully on or off.\n",
    "* This is because qubits can never be simultaneously certain about each kind of output.\n",
    "* If they are certain about one, the other must be random.\n",
    "* Quantum computing is all about making sure that your certainty and your randomness in the right place.\n",
    "\n",
    "### Exercise\n",
    "*  Move the off to the top and the randomness to the bottom."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0']]\n",
    "success_condition = {'IX': 1.0}\n",
    "allowed_gates = {'0': {}, '1': {'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[0], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 9\n",
    "\n",
    "### Intro\n",
    "* We can also share the limited certainty of our qubits between the two outputs.\n",
    "* Then both can be mostly certain about the output they give, even of they aren't fully certain.\n",
    "* In this exercise, the two circles for q[1] will start off dark grey. This means that both outputs would be highly likely, but not certain, to output a `0`.\n",
    "\n",
    "### Exercise\n",
    "* Make the two circles for q[1] both light grey. This means that they'd be highly likely, but not certain, to output a `1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['ry(pi/4)', '1']]\n",
    "success_condition = {'IZ': -0.7071, 'IX': -0.7071}\n",
    "allowed_gates = {'0': {}, '1': {'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[0], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 10\n",
    "\n",
    "### Intro\n",
    "* Now you know the basic tools, you can tackle both qubits at once.\n",
    "\n",
    "### Exercise\n",
    "*  Make both bottom outputs random."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '1']]\n",
    "success_condition = {'ZI': 0.0, 'IZ': 0.0}\n",
    "allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, False]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Each bottom output here would randomly output a `0` or a `1`.\n",
    "* But will their outputs be correlated? Anti-correlated? Completely unrelated?\n",
    "* Just as we did with bits, we'll keep track of this information with some extra circles.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 11\n",
    "\n",
    "### Intro\n",
    "* In this puzzle you'll see two four new circles.\n",
    "* You've already been introduced to the lower one Level 1.\n",
    "* It does the same job here, keeping track of whether the bottom output of one qubit will agree with the bottom ouput of the other.\n",
    "* If the two bottom outputs would definitely agree, this circle is off (black). If they'd disagree, it's on (white).\n",
    "\n",
    "### Exercise\n",
    "*  Make the bottom outputs certain to disagree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h','0'],['h','1']]\n",
    "success_condition = {'ZZ': -1.0}\n",
    "allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 12\n",
    "\n",
    "### Intro\n",
    "* The new circle at the very top has a similar job.\n",
    "* It keeps track of whether the top output from one qubit would agree with the top output from the other.\n",
    "\n",
    "### Exercise\n",
    "*  Make the top outputs certain to agree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x','0']]\n",
    "success_condition = {'XX': 1.0}\n",
    "allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 13\n",
    "\n",
    "### Intro\n",
    "* The other new circles tell us whether the bottom output for one qubit would agree with the top one from the other.\n",
    "\n",
    "### Exercise\n",
    "*  Make the top output for q[0] certain to disagree with the bottom output for q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'XZ': -1.0}\n",
    "allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 14\n",
    "\n",
    "### Intro\n",
    "* Notice how the the `x`, `z` and `h` gates affect the new circles.\n",
    "* Specifically, the `x` gates don't just flip a single circle between black and white, but a whole column full of them.\n",
    "\n",
    "### Exercise\n",
    "*  Turn the two bottom outputs on as much as you can."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['ry(-pi/4)', '1'], ['ry(-pi/4)','0']]\n",
    "success_condition = {'ZI': -0.7071, 'IZ': -0.7071}\n",
    "allowed_gates = {'0': {'x': 0}, '1': {'x': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 15\n",
    "\n",
    "### Intro\n",
    "* The `z` gates affect the top columns in a similar way.\n",
    "* The `h` gate swaps the bottom and top columns.\n",
    "\n",
    "### Exercise\n",
    "*  Turn off the top cirlces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '1'], ['x','0']]\n",
    "success_condition = {'XI':1, 'IX':1}\n",
    "allowed_gates = {'0': {'z': 0, 'h': 0}, '1': {'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Level 3: Two qubit gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1\n",
    "\n",
    "### Introduction\n",
    "* In the exercises on bits, we used the `CNOT` gate.\n",
    "* This can be used on qubits too!\n",
    "* Since the `x` gate serves as our quantum `NOT`, we will use the `cx` gate to do a `CNOT` on qubits.\n",
    "* Again, these have a _control_ and a _target_: The bottom circle of the control qubit decides whether an `x` is applied to the target qubit.\n",
    "* When you apply this gate, the qubit you choose will serve as the target. The other qubit will then be the control.\n",
    "\n",
    "###  Exercise\n",
    "* Use a `cx` or two to turn on the bottom circle of q[1], and turn off the bottom circle of q[0]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [['x', '0']]\n",
    "success_condition = {'ZI': 1.0, 'IZ': -1.0}\n",
    "allowed_gates = {'0': {'cx': 0}, '1': {'cx': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 2\n",
    "\n",
    "### Introduction\n",
    "* As well as a `cx` gate, there is also the `cz`.\n",
    "* This applies a `z` to the target when the bottom circle of the control is on\n",
    "\n",
    "###  Exercise\n",
    "* Turn on the top circle of q[0], and turn off the bottom circle of q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0'],['x', '1']]\n",
    "success_condition = {'XI': -1.0, 'IZ': 1.0}\n",
    "allowed_gates = {'0': {'h': 0, 'cz': 0}, '1': {'cx': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 3\n",
    "\n",
    "### Introduction\n",
    "* There's another way that we can explain what a `cz` is doing.\n",
    "   - It swaps the top circle of q[0] with the neighbouring circle above it.\n",
    "   - It does the same with the top circle of q[1].\n",
    "* It also does something weird with the circle at the top of the grid.\n",
    "\n",
    "###  Exercise\n",
    "* Do the `cz` twice with each qubit as control, and see what happens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [['h', '0'],['x', '1'],['h', '1']]\n",
    "success_condition = { }\n",
    "allowed_gates = {'0':{'cz': 2}, '1':{'cz': 2}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Outro\n",
    "\n",
    "* As you might have noticed, it doesn't matter which qubit you choose as control: the `cz` does the same thing in either case.\n",
    "* So from now on, choosing the control qubit for the `cz` is not required."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 3b\n",
    "\n",
    "### Introduction\n",
    "* As mentioned earlier, the top and bottom circles correspond to two ways of getting an output from a qubit.\n",
    "* There is actually also a third way, known as the y measurement.\n",
    "* This tutorial ignores the y measurements, to make things a little simpler.\n",
    "* The only time their absence can be noticed is when `cx` or `cz` gates are used.\n",
    "* The 'something weird with the circle at the top of the grid' is not so weird when we add in a middle line of circles for the outputs of y measurements.\n",
    "* Do the same puzzle as before with these new circles, and see for yourself.\n",
    "\n",
    "###  Exercise\n",
    "* Do the `cz` twice and see what happens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0'],['x', '1'],['h', '1']]\n",
    "success_condition = { }\n",
    "allowed_gates = {'0': {}, '1': {}, 'both': {'cz': 2}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='y')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "\n",
    "* The circle at the top, is replaced by the one in the middle.\n",
    "* It only seemed strange before because the middle rows where missing.\n",
    "* By default, we'll do without these rows from now on.\n",
    "* You can add them into puzzles yourself by using the `mode='y'` argument in `hello_quantum.run_game()`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 4\n",
    "\n",
    "### Introduction\n",
    "* In a previous exercise, you've built an `x` from a `z` and some `h`s.\n",
    "* In the same way, it's possible to build a `cx` from a `cz` and some `h`s.\n",
    "\n",
    "###  Exercise\n",
    "* Turn on the bottom circle of q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x', '0']]\n",
    "success_condition = {'IZ': -1.0}\n",
    "allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz': 0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "\n",
    "* Unlike the `cz`, the `cx` is not symmetric.\n",
    "* If you instead wanted to make a `cx` whose target was `q[0]`, you would have to do the `h`s on `q[0]` instead."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 5\n",
    "\n",
    "### Introduction\n",
    "* Because the `cx` isn't symmetric, let's try to interpret it 'backwards'.\n",
    "* So instead of thinking of it as doing an `x` on the target depending on what the bottom circle of the control is doing...\n",
    "* ... we can think of it as doing a `z` to the control depending on what the top circle of the target is doing.\n",
    "* With this interpretation, it seems like it is the target doing the controlling, and the control being the target!\n",
    "* In case you don't believe me, here's an exercise to test out this very property\n",
    "\n",
    "###  Exercise\n",
    "* Turn on the top circle of q[0]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h', '0'],['h', '1']]\n",
    "success_condition = {'XI': -1.0, 'IX': -1.0}\n",
    "allowed_gates = {'0': {}, '1': {'z':0,'cx': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* So there's two different stories about how the `cx` works. Though they may seem to be contradictory, they are equally good descriptions.\n",
    "* This is a great example of the weird and wonderful nature of quantum gates."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 6\n",
    "\n",
    "### Introduction\n",
    "* These two interpretations of a `cx` can help us do something pretty useful: turning one around.\n",
    "* Suppose you need a `cx` with q[1] as target, but you can only do one with q[0] as target.\n",
    "* Can we somehow get the effect we need?\n",
    "\n",
    "###  Exercise\n",
    "* Turn on the bottom circle of q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'IZ': -1.0}\n",
    "allowed_gates = {'0': {'x':0,'h':0,'cx':0}, '1': {'h':0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* If you remember anything from these exercises, it should probably be this.\n",
    "* It is common for real qubit devices to limit which way around you can do the `cx`.\n",
    "* So the ability to turn them around comes in very handy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 7\n",
    "\n",
    "### Introduction\n",
    "* The `cz` and `cx` gates can also be used to make a `swap`.\n",
    "* This does exactly what the name suggests: it swaps the states of two qubits.\n",
    "\n",
    "###  Exercise\n",
    "* Swap the two qubits:\n",
    "    - Make the bottom circle white and the top circle grey for q[0];\n",
    "    - Make the bottom circle dark grey and the top circle light grey for q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['ry(-pi/4)','0'],['ry(-pi/4)','0'],['ry(-pi/4)','0'],['x','0'],['x','1']]\n",
    "success_condition = {'ZI': -1.0,'XI':0,'IZ':0.7071,'IX':-0.7071}\n",
    "allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz': 0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Outro\n",
    "* Note that your solution to this puzzle might not have been a general purpose `swap`.\n",
    "* Compare your solution to those for the next few puzzles, which also implement swaps."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 8\n",
    "\n",
    "#### Intro\n",
    "* Another puzzle based on the `swap`.\n",
    "\n",
    "#### Exercise\n",
    "* Swap the two qubits:\n",
    "    * Make the top circle black for q[0].\n",
    "    * Make the bottom white for q[1].\n",
    "* And do it with 3 `cz` gates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x','0'],['h','1']]\n",
    "success_condition = {'IX':1,'ZI':-1}\n",
    "allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz':3}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names,shots=2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 9\n",
    "\n",
    "#### Intro\n",
    "* Another puzzle based on the `swap`.\n",
    "\n",
    "#### Exercise\n",
    "* Swap the two qubits:\n",
    "    * Turn off the bottom circle for q[0].\n",
    "    * Turn on the bottom circle q[1]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [['x','1']]\n",
    "success_condition = {'IZ':1.0,'ZI':-1.0}\n",
    "allowed_gates = {'0': {'h':0}, '1': {'h':0}, 'both': {'cz':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names,shots=2000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Level 4: Beyond Clifford gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1a\n",
    "\n",
    "### Introduction\n",
    "* The gates you've seen so far are called the 'Clifford gates'.\n",
    "* They are very important for moving and manipulating information in quantum computers\n",
    "* But to create algorithms that will outperform standard computers, we need more gates\n",
    "* This puzzle has one for you to try. Simply do it a few times, and see if you can work out what it does.\n",
    "\n",
    "###  Exercise\n",
    "* Apply `ry(pi/4)` four times to q[0]."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {}\n",
    "allowed_gates = {'0': {'ry(pi/4)': 4}, '1': {}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Outro\n",
    "* If you were able to work it out, you are some sort of genuis!\n",
    "* For those of us who are mere mortals, let's try something new to help us figure it out."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1b\n",
    "\n",
    "### Introduction\n",
    "* To understand this gate, we need to slightly change the way we visualize the qubits.\n",
    "* From now now, an output that is certain to give `0` will be represented by a white line rather than a white circle.\n",
    "* And an output certain to give `1` will be a black line instead of a black circle.\n",
    "* For a random output, you'll see a line that's part white and part black instead of a grey circle.\n",
    "* Here's an old exercise to help you get used to this new visualization.\n",
    "\n",
    "###  Exercise\n",
    "* Make the top outputs certain to agree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x','0']]\n",
    "success_condition = {'XX': 1.0}\n",
    "allowed_gates = {'0': {'x': 0, 'z': 0, 'h': 0}, '1': {'x': 0, 'z': 0, 'h': 0}, 'both': {}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 1c\n",
    "\n",
    "### Introduction\n",
    "* In this puzzle you'll see a new gate: `bloch`.\n",
    "* This doesn't actually do anything to the qubits. It just draws the two lines for each qubit on top of each other.\n",
    "* It also puts a point where their Puzzles intersect.\n",
    "* Using `bloch`, you should hopefully be able to figure out how `ry(pi/4)` works.\n",
    "\n",
    "###  Exercise\n",
    "* Turn the bottom line of q[0] fully on, and use the `bloch` gate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'ZI': -1.0}\n",
    "allowed_gates = {'0': {'bloch':1, 'ry(pi/4)': 0}, '1':{}, 'both': {'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Outro\n",
    "* As you probably noticed, this doesn't just combine the two lines for each qubit. It combines their whole columns.\n",
    "* If we follow the points, the effect of `ry(pi/4)` is to rotate them.\n",
    "* Each application moves it an eighth of the way around the circle, and moves the Puzzles of the lines along with it.\n",
    "* The `ry(-pi/4)` gate is the same, except the rotation is in the other direction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 2\n",
    "\n",
    "### Introduction\n",
    "* Now let's use these gates on the other qubit too.\n",
    "\n",
    "###  Exercise\n",
    "* Turn the bottom lines fully on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = [['h','0'],['h','1']]\n",
    "success_condition = {'ZI': -1.0,'IZ': -1.0}\n",
    "allowed_gates = {'0': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'qubit1'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 3\n",
    "\n",
    "### Introduction\n",
    "* Here's a puzzle you could solve with a simple `cx`, or a `cz` and some `h`s.\n",
    "* Unfortunately you have neither `cx` nor `h`.\n",
    "* So you'll need to work out how `cz` and `ry`s can do the job.\n",
    "\n",
    "\n",
    "###  Exercise\n",
    "* Make the bottom outputs agree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['h','0']]\n",
    "success_condition = {'ZZ': 1.0}\n",
    "allowed_gates = {'0': {}, '1': {'bloch':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'unbloch':0,'cz':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 4\n",
    "\n",
    "### Introduction\n",
    "* Using `x`s or `z`s you can effectively _reflect_ an `ry`, to make it move in the opposite direction.\n",
    "\n",
    "\n",
    "###  Exercise\n",
    "* Turn the bottom outputs fully off with just one `ry(pi/4)` on each."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['ry(pi/4)','0'],['ry(pi/4)','1']]\n",
    "success_condition = {'ZI': 1.0,'IZ': 1.0}\n",
    "allowed_gates = {'0': {'bloch':0, 'z':0, 'ry(pi/4)': 1}, '1': {'bloch':0, 'x':0, 'ry(pi/4)': 1}, 'both': {'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Puzzle 5\n",
    "\n",
    "### Introduction\n",
    "* With the `ry`s, we can make conditional gates that are more interesting than just `cz` and `cx`.\n",
    "* For example, we can make a controlled-`h`.\n",
    "\n",
    "\n",
    "###  Exercise\n",
    "* Turn off the bottom output for q[1] using exactly one `ry(pi/4)` and `ry(-pi/4)` on that qubit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = [['x','0'],['h','1']]\n",
    "success_condition = {'IZ': 1.0}\n",
    "allowed_gates = {'0': {}, '1': {'bloch':0, 'cx':0, 'ry(pi/4)': 1, 'ry(-pi/4)': 1}, 'both': {'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(puzzle).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sandbox\n",
    "\n",
    "You now know enough basic quantum gates to build fully powerful quantum programs. You'll get a taste of this in the next Level. Until then, here are two grids with all the gates enabled, so you can have a play around.\n",
    "\n",
    "Here's one with the line-based visualization, to help with the non-Clifford gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'IZ': 1.0,'IX': 1.0}\n",
    "allowed_gates = {'0': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'cz':0, 'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "line_sandbox = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(line_sandbox).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is a grid with the middle lines, which describe outputs for y measurements. With this you can also try some new non-Clifford gates: 'rx(pi/4)' and 'rx(-pi/4)'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'IZ': 1.0,'IX': 1.0}\n",
    "allowed_gates = {'0': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0, 'rx(pi/4)': 0, 'rx(-pi/4)': 0}, '1': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0, 'rx(pi/4)': 0, 'rx(-pi/4)': 0}, 'both': {'cz':0, 'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'q[0]', '1':'q[1]'}\n",
    "y_sandbox = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='y')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_quantum.get_circuit(y_sandbox).draw()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Level 5: Proving the Uniqueness of Quantum Variables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bell test for classical variables\n",
    "\n",
    "Here we'll investigate how quantum variables (based on qubits) differ from standard ones (based on bits).\n",
    "\n",
    "We'll do this by creating a pair of variables, which we will call `A` and `B`. We aren't going to put any conditions on what these can be, or how they are initialized. So there are a lot of possibilities:\n",
    "\n",
    "* They could be any kind of variable, such as\n",
    "    * `int`\n",
    "    * `list`\n",
    "    * `dict`\n",
    "    * ...\n",
    "* They could be initialized by any kind of process, such as\n",
    "    * left empty\n",
    "    * filled with a given set of values\n",
    "    * generated by a given random process\n",
    "        * indepedently applied to `A` and `B`\n",
    "        * applied to `A` and `B` together, allowing for correlations between their randomness\n",
    "\n",
    "If the variables are initialized by a random process, it means they'll have different values every time we run our program. This is perfectly fine. The only rule we need to obey is that the process of generating the randomness is the same for every run.\n",
    "\n",
    "We'll use the function below to set up these variables. This currently has `A` and `B` defined as to be partially correlated random floating point numbers. But you can change it to whatever you want."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "def setup_variables ():\n",
    "    \n",
    "    ### Replace this section with anything you want ###\n",
    "    \n",
    "    r = random.random()\n",
    "    \n",
    "    A = r*(2/3)\n",
    "    B = r*(1/3)\n",
    "    \n",
    "    ### End of section ###\n",
    "    \n",
    "    return A, B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our next job is to define a hashing function. This simply needs to take one of the variables as input, and then give a bit value as an output.\n",
    "\n",
    "This function must also be capable of performing two different types of hash. So it needs to be able to be able to chew on a variable and spit out a bit in to different ways. Another input to the function is therefore the kind of hash we want to use.\n",
    "\n",
    "To be consistent with the rest of the program, the two possible hash types should be called `'H'` and `'V'`. Also, the output must be in the form of a single value bit string: either `'0'` or `'1'`.\n",
    "\n",
    "In the (fairly arbitrary) example given, the bits were created by comparing `A` and `B` to a certain value. The output is `'1'` if they are under that value, and `'0'` otherwise. The type of hash determines the value used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hash2bit ( variable, hash ):\n",
    "    \n",
    "    ### Replace this section with anything you want ###\n",
    "    \n",
    "    if hash=='V':\n",
    "        bit = (variable<0.5)\n",
    "    elif hash=='H':\n",
    "        bit = (variable<0.25)\n",
    "        \n",
    "    bit = str(int(bit))\n",
    "    \n",
    "    ### End of section ###\n",
    "        \n",
    "    return bit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once these are defined, there are four quantities we wish to calculate: `P['HH']`, `P['HV']`, `P['VH']` and `P['VV']`.\n",
    "\n",
    "Let's focus on `P['HV']` as an example. This is the probability that the bit value derived from an `'H'` type hash on `A` is the same as that from a `'V'` type has on `B`. We will estimate this probability by sampling many times and determining the fraction of samples for which the corresponding bit values agree.\n",
    "\n",
    "The other probabilities are defined similarly: `P['HH']` compares a `'H'` type hash on both `A` and `B`, `P['VV']` compares a `V` type hash on both, and `P['VH']` compares a `V` type hash on `A` with a `H` type has on `B`.\n",
    "\n",
    "These probabilities are calculated in the following function, which returns all the values of `P` in a dictionary. The parameter `shots` is the number of samples we'll use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shots = 8192\n",
    "def calculate_P ( ):\n",
    "    \n",
    "    P = {}\n",
    "    for hashes in ['VV','VH','HV','HH']:\n",
    "        \n",
    "        # calculate each P[hashes] by sampling over `shots` samples\n",
    "        P[hashes] = 0\n",
    "        for shot in range(shots):\n",
    "\n",
    "            A, B = setup_variables()\n",
    "\n",
    "            a = hash2bit ( A, hashes[0] ) # hash type for variable `A` is the first character of `hashes`\n",
    "            b = hash2bit ( B, hashes[1] ) # hash type for variable `B` is the second character of `hashes`\n",
    "\n",
    "            P[hashes] += (a!=b) / shots\n",
    " \n",
    "    return P"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's actually calculate these values for the method we have chosen to set up and hash the variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "P = calculate_P()\n",
    "print(P)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These values will vary slightly from one run to the next due to the fact that we only use a finite number of shots. To change them significantly, we need to change the way the variables are initiated, and/or the way the hash functions are defined.\n",
    "\n",
    "No matter how these functions are defined, there are certain restrictions that the values of `P` will always obey.\n",
    "\n",
    "For example, consider the case that `P['HV']`, `P['VH']` and `P['VV']` are all `0.0`. The only way that this can be possible is for `P['HH']` to also be `0.0`.\n",
    "\n",
    "To see why, we start by noting that `P['HV']=0.0` is telling us that `hash2bit ( A, H )` and `hash2bit ( B, V )` were never different in any of the runs. So this means we can always expect them to be equal.\n",
    "\n",
    "    hash2bit ( A, H ) = hash2bit ( B, V )        (1)\n",
    "    \n",
    "From `P['VV']=0.0` and `P['VH']=0.0` we can similarly get\n",
    "\n",
    "    hash2bit ( A, V ) = hash2bit ( B, V )        (2)\n",
    "    \n",
    "    hash2bit ( A, V ) = hash2bit ( B, H )        (3)\n",
    "    \n",
    "Putting (1) and (2) together implies that\n",
    "\n",
    "    hash2bit ( A, H ) = hash2bit ( A, V )        (4)\n",
    "    \n",
    "Combining this with (3) gives\n",
    "\n",
    "    hash2bit ( A, H ) = hash2bit ( B, H )        (5)\n",
    "\n",
    "And if these values are always equal, we'll never see a run in which they are different. This is exactly what we set out to prove: `P['HH']=0.0`.\n",
    "\n",
    "More generally, we can use the values of `P['HV']`, `P['VH']` and `P['VV']` to set an upper limit on what `P['HH']` can be. By adapting the [CHSH inequality](https://en.wikipedia.org/wiki/CHSH_inequality) we find that\n",
    "\n",
    "$\\,\\,\\,\\,\\,\\,\\,$ `P['HH']` $\\, \\leq \\,$ `P['HV'] + P['VH'] + P['VV']`\n",
    "\n",
    "This is not just a special property of `P['HH']`. It's also true for all the others: each of these probabilities cannot be greater than the sum of the others.\n",
    "\n",
    "To test whether this logic holds, we'll see how well the probabilities obey these inequalities. Note that we might get slight violations due to the fact that our the `P` values aren't exact, but are estimations made using a limited number of samples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bell_test (P):\n",
    "    \n",
    "    sum_P = sum(P.values())\n",
    "    for hashes in P:\n",
    "        \n",
    "        bound = sum_P - P[hashes]\n",
    "        \n",
    "        print(\"The upper bound for P['\"+hashes+\"'] is \"+str(bound))\n",
    "        print(\"The value of P['\"+hashes+\"'] is \"+str(P[hashes]))\n",
    "        if P[hashes]<=bound:\n",
    "            print(\"The upper bound is obeyed :)\\n\")\n",
    "        else:\n",
    "            if P[hashes]-bound < 0.1:\n",
    "                print(\"This seems to have gone over the upper bound, but only by a little bit :S\\nProbably just rounding errors or statistical noise.\\n\")\n",
    "            else:\n",
    "                print(\"!!!!! This has gone well over the upper bound :O !!!!!\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bell_test(P)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With the initialization and hash functions provided in this notebook, the value of `P('HV')` should be pretty much the same as the upper bound. Since the numbers are estimated statistically, and therefore are slightly approximate due to statistical noise, you might even see it go a tiny bit over. But you'll never see it significantly surpass the bound.\n",
    "\n",
    "If you don't believe me, try it for yourself. Change the way the variables are initialized, and how the hashes are calculated, and try to get one of the bounds to be significantly broken."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bell test for quantum variables\n",
    "\n",
    "Now we are going to do the same thing all over again, except our variables `A` and `B` will be quantum variables. Specifically, they'll be the simplest kind of quantum variable: qubits."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When writing quantum programs, we have to set up our qubits and bits before we can use them. This is done by the function below. It defines a register of two bits, and assigns them as our variables `A` and `B`. It then sets up a register of two bits to receive the outputs, and assigns them as `a` and `b`.\n",
    "\n",
    "Finally it uses these registers to set up an empty quantum program. This is called `qc`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\n",
    "\n",
    "def initialize_program ():\n",
    "    \n",
    "    qubit = QuantumRegister(2)\n",
    "    A = qubit[0]\n",
    "    B = qubit[1]\n",
    "    \n",
    "    bit = ClassicalRegister(2)\n",
    "    a = bit[0]\n",
    "    b = bit[1]\n",
    "    \n",
    "    qc = QuantumCircuit(qubit, bit)\n",
    "    \n",
    "    return A, B, a, b, qc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we start writing the quantum program to set up our variables, let's think about what needs to happen at the end of the program. This will be where we define the different hash functions, which turn our qubits into bits.\n",
    "\n",
    "The simplest way to extract a bit from a qubit is through the `measure` gate. This corresponds to the bottom circle of a qubit in the visualization we've been using. Let's use this as our `V` type hash.\n",
    "\n",
    "For the output that corresponds to the top circle, there is no direct means of access. However, we can do it indirectly by first doing an `h` to swap the top and bottom circles, and then using the `measure` gate. This will be our `H` type hash.\n",
    "\n",
    "Note that this function has more inputs that its classical counterpart. We have to tell it the `bit` on which to write the result, and the quantum program, `qc`, on which we write the gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hash2bit  ( variable, hash, bit, qc ):\n",
    "    \n",
    "    if hash=='H':\n",
    "        qc.h( variable )\n",
    "        \n",
    "    qc.measure( variable, bit )\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now its time to set up the variables `A` and `B`. To write this program, you can use the grid below. You can either follow the suggested exercise, or do whatever you like. Once you are ready, just move on. The cell containing the `setup_variables()` function, will then use the program you wrote with the grid.\n",
    "\n",
    "Note that our choice of means that the probabilities `P['HH']`, `P['HV']`, `P['VH']` and `P['VV']` will explicitly correspond to circles on our grid. For example, the circle at the very top tells us how likely the two top outputs would be to disagree. If this is white, then `P['HH']=1` , if it is black then `P['HH']=0`.\n",
    "\n",
    "### Exercise\n",
    "* Make it so that outputs from the top circles of both qubits are most likely to disagree, whereas all other combinations of outputs are most likely to agree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "initialize = []\n",
    "success_condition = {'ZZ':+0.7071,'ZX':+0.7071,'XZ':+0.7071,'XX':-0.7071}\n",
    "allowed_gates = {'0': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, '1': {'bloch':0, 'x':0, 'z':0, 'h':0, 'cx':0, 'ry(pi/4)': 0, 'ry(-pi/4)': 0}, 'both': {'cz':0, 'unbloch':0}}\n",
    "vi = [[], True, True]\n",
    "qubit_names = {'0':'A', '1':'B'}\n",
    "puzzle = hello_quantum.run_game(initialize, success_condition, allowed_gates, vi, qubit_names, mode='line')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the program as written above will be used to set up the quantum variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "def setup_variables ( A, B, qc ):\n",
    "    \n",
    "    for line in puzzle.program:\n",
    "        eval(line)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The values of `P` are calculated in the function below. This is done by sending jobs to IBM via Qiskit and getting results which tell us how many of the samples gave each possible result. The results are given as a bit string, `string`, which Qiskit numbers from right to left. This means that the value of `a`, which corresponds to `bit[0]` is the first from the right\n",
    "\n",
    "    a = string[-1]\n",
    "    \n",
    "and the value of `b` is right next to it at the second from the right\n",
    "\n",
    "    b = string[-2]\n",
    "\n",
    "The number of samples for this bit string is provided by the dictionary of results, `stats`, as `stats[string]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shots = 8192\n",
    "from qiskit import execute\n",
    "\n",
    "def calculate_P ( backend ):\n",
    "    \n",
    "    P = {}\n",
    "    program = {}\n",
    "    for hashes in ['VV','VH','HV','HH']:\n",
    "\n",
    "        A, B, a, b, program[hashes] = initialize_program ()\n",
    "\n",
    "        setup_variables( A, B, program[hashes] )\n",
    "\n",
    "        hash2bit ( A, hashes[0], a, program[hashes])\n",
    "        hash2bit ( B, hashes[1], b, program[hashes])\n",
    "            \n",
    "    # submit jobs\n",
    "    job = execute( list(program.values()), backend, shots=shots )\n",
    "\n",
    "    # get the results\n",
    "    for hashes in ['VV','VH','HV','HH']:\n",
    "        stats = job.result().get_counts(program[hashes])\n",
    "        \n",
    "        P[hashes] = 0\n",
    "        for string in stats.keys():\n",
    "            a = string[-1]\n",
    "            b = string[-2]\n",
    "            \n",
    "            if a!=b:\n",
    "                P[hashes] += stats[string] / shots\n",
    "\n",
    "    return P"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now its time to choose and set up the actually device we are going to use. By default, we'll use a simulator. You could instead use a real cloud-based device by changing the backend accordingly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit import Aer\n",
    "device = 'qasm_simulator'\n",
    "backend = Aer.get_backend(device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "P = calculate_P( backend )\n",
    "print(P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bell_test( P )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "If you prepared the state suggestion by the exercise, you will have found a significant violation of the upper bound for `P['HH']`. So what is going on here? The chain of logic we based the Bell test on obviously doesn't apply to quantum variables. But why?\n",
    "\n",
    "The answer is that there is a hidden assumption in that logic. To see why, let's revisit point (4).\n",
    "\n",
    "    hash2bit ( A, H ) = hash2bit ( A, V )        (4)\n",
    "    \n",
    "Here we compare the value we'd get from an `H` type of hash of the variable `A` with that for a `V` type hash.\n",
    "\n",
    "For classical variables, this is perfectly sensible. There is nothing stopping us from calculating both hashes and comparing the results. Even if calculating the hash of a variable changes the variable, that's not a problem. All we need to do is copy it beforehand and we can do both hashes without any issue.\n",
    "\n",
    "The same is not true for quantum variables. The result of the hashes is not known until we actually do them. It's only then that the qubit actually decides what bit value to give. And once it decides the value for one type of hash, we can never determine what it would have decided if we had used another type of hash. We can't get around this by copying the quantum variables either, because quantum variables [cannot be copied](https://en.wikipedia.org/wiki/No-cloning_theorem). This means there is no context in which the values `hash2bit(A,H)` and `hash2bit(A,V)` are well-defined at the same time, and so it is impossible to compare them.\n",
    "\n",
    "Another hidden assumption is that `hash2bit(A,hash)` depends only on the type of hash chosen for variable `A`, and not the one chosen for variable `B`. This is also perfectly sensible, since this exactly the way we set up the `hash2bit()` function. However, the very fact that the upper bound was violated does seem to imply that each variable knows what hash is  being done to the other, so they they can conspire to give very different behaviour when both have a `H` type hash.\n",
    "\n",
    "Even so, the effect is subtle. It is impossible to determine which variable is affecting which: You can change the order in which the hashes are done, or [effectively do them at the same time](https://en.wikipedia.org/wiki/Loopholes_in_Bell_test_experiments#Communication,_or_locality), and you'll get the same results. This means that it not clear whether we can say they affect each other (see [here](https://quantumcomputing.stackexchange.com/questions/2028/is-it-true-to-say-that-one-qubit-in-an-entangled-state-can-instantaneously-affec) for some discussion). But what we can say is that they are [contextual](https://en.wikipedia.org/wiki/Quantum_contextuality): to fully understand results from one variable, it is sometimes required to look at what was done to another.\n",
    "\n",
    "All this goes to show that quantum variables don't always follow the logic we are used to. They follow different rules, the rules of quantum mechanics, which will allow us to find ways of performing computation in very different ways than we are used to."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "keywords = {'Topics': ['Games', 'Bell inequality', 'Entanglement'], 'gates': ['`x`', '`z`', '`h`', '`cx`']}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
